#include "base/pickle.h"
#include "base/types.h"

#include "cppunit/TestFixture.h"
#include "cppunit/extensions/HelperMacros.h"

using namespace base;

class PickleTest : public CppUnit::TestFixture {
    CPPUNIT_TEST_SUITE(PickleTest);
    CPPUNIT_TEST(testBool);
    CPPUNIT_TEST(testInt8);
    CPPUNIT_TEST(testInt16);
    CPPUNIT_TEST(testInt32);
    CPPUNIT_TEST(testInt64);
    CPPUNIT_TEST(testUInt8);
    CPPUNIT_TEST(testUInt16);
    CPPUNIT_TEST(testUInt32);
    CPPUNIT_TEST(testUInt64);
    CPPUNIT_TEST(testMixedInts);
    CPPUNIT_TEST(testFloat);
    CPPUNIT_TEST(testDouble);
    CPPUNIT_TEST(testMixedNumbers);
    CPPUNIT_TEST(testBuffer);
    CPPUNIT_TEST_SUITE_END();

public:
    void setUp() {
    }

    void tearDown() {
    }

    void testBool() {
        Pickle pickle;

        pickle.WriteBool(true);
        pickle.WriteBool(false);
        pickle.WriteBool(false);
        pickle.WriteBool(true);

        CPPUNIT_ASSERT_EQUAL(true, pickle.ReadBool());
        CPPUNIT_ASSERT_EQUAL(false, pickle.ReadBool());
        CPPUNIT_ASSERT_EQUAL(false, pickle.ReadBool());
        CPPUNIT_ASSERT_EQUAL(true, pickle.ReadBool());
    }

    void testInt8() {
        const int8 kMax = 100;
        const int8 kMin = -100;

        Pickle pickle;

        for (int8 i = kMin; i < kMax; i++) {
            pickle.WriteInt8(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int8)),
                             pickle.Size());

        for (int8 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadInt8());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int8)),
                             pickle.Size());
    }

    void testInt16() {
        const int16 kMax = 1000;
        const int16 kMin = -1000;

        Pickle pickle;

        for (int16 i = kMin; i < kMax; i++) {
            pickle.WriteInt16(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int16)),
                             pickle.Size());

        for (int16 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadInt16());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int16)),
                             pickle.Size());
    }

    void testInt32() {
        const int32 kMax = 1000;
        const int32 kMin = -1000;

        Pickle pickle;

        for (int32 i = kMin; i < kMax; i++) {
            pickle.WriteInt32(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int32)),
                             pickle.Size());

        for (int32 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadInt32());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int32)),
                             pickle.Size());
    }

    void testInt64() {
        const int64 kMax = 1000;
        const int64 kMin = -1000;

        Pickle pickle;

        for (int64 i = kMin; i < kMax; i++) {
            pickle.WriteInt64(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int64)),
                             pickle.Size());

        for (int64 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadInt64());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(int64)),
                             pickle.Size());
    }

    void testUInt8() {
        const uint8 kMax = 100;
        const uint8 kMin = 0;

        Pickle pickle;

        for (uint8 i = kMin; i < kMax; i++) {
            pickle.WriteUInt8(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint8)),
                             pickle.Size());

        for (uint8 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadUInt8());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint8)),
                             pickle.Size());
    }

    void testUInt16() {
        const uint16 kMax = 1000;
        const uint16 kMin = 0;

        Pickle pickle;

        for (uint16 i = kMin; i < kMax; i++) {
            pickle.WriteUInt16(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint16)),
                             pickle.Size());

        for (uint16 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadUInt16());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint16)),
                             pickle.Size());
    }

    void testUInt32() {
        const uint32 kMax = 1000;
        const uint32 kMin = 0;

        Pickle pickle;

        for (uint32 i = kMin; i < kMax; i++) {
            pickle.WriteUInt32(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint32)),
                             pickle.Size());

        for (uint32 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadUInt32());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint32)),
                             pickle.Size());
    }

    void testUInt64() {
        const uint64 kMax = 1000;
        const uint64 kMin = 0;

        Pickle pickle;

        for (uint64 i = kMin; i < kMax; i++) {
            pickle.WriteUInt64(i);
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint64)),
                             pickle.Size());

        for (uint64 i = kMin; i < kMax; i++) {
            CPPUNIT_ASSERT_EQUAL(i, pickle.ReadUInt64());
        }

        CPPUNIT_ASSERT_EQUAL(static_cast<int>((kMax - kMin)*sizeof(uint64)),
                             pickle.Size());
    }

    void testMixedInts() {
        Pickle pickle;

        pickle.WriteInt8(-1);
        pickle.WriteInt16(-10);
        pickle.WriteInt32(-100);
        pickle.WriteInt64(-1000);
        pickle.WriteInt8(0);
        pickle.WriteUInt8(0);
        pickle.WriteInt8(1);
        pickle.WriteUInt8(1);
        pickle.WriteInt16(0);
        pickle.WriteUInt16(0);
        pickle.WriteInt16(10);
        pickle.WriteUInt16(10);
        pickle.WriteInt32(0);
        pickle.WriteUInt32(0);
        pickle.WriteInt32(100);
        pickle.WriteUInt32(100);
        pickle.WriteInt64(0);
        pickle.WriteUInt64(0);
        pickle.WriteInt64(1000);
        pickle.WriteUInt64(1000);

        pickle.WriteInt8(kMinInt8);
        pickle.WriteInt8(kMaxInt8);
        pickle.WriteUInt8(kMaxUInt8);
        pickle.WriteInt16(kMinInt16);
        pickle.WriteInt16(kMaxInt16);
        pickle.WriteUInt16(kMaxUInt16);
        pickle.WriteInt32(kMinInt32);
        pickle.WriteInt32(kMaxInt32);
        pickle.WriteUInt32(kMaxUInt32);
        pickle.WriteInt64(kMinInt64);
        pickle.WriteInt64(kMaxInt64);
        pickle.WriteUInt64(kMaxUInt64);

        CPPUNIT_ASSERT_EQUAL(static_cast<int8>(-1), pickle.ReadInt8());
        CPPUNIT_ASSERT_EQUAL(static_cast<int16>(-10), pickle.ReadInt16());
        CPPUNIT_ASSERT_EQUAL(static_cast<int32>(-100), pickle.ReadInt32());
        CPPUNIT_ASSERT_EQUAL(static_cast<int64>(-1000), pickle.ReadInt64());
        CPPUNIT_ASSERT_EQUAL(static_cast<int8>(0), pickle.ReadInt8());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint8>(0), pickle.ReadUInt8());
        CPPUNIT_ASSERT_EQUAL(static_cast<int8>(1), pickle.ReadInt8());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint8>(1), pickle.ReadUInt8());
        CPPUNIT_ASSERT_EQUAL(static_cast<int16>(0), pickle.ReadInt16());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(0), pickle.ReadUInt16());
        CPPUNIT_ASSERT_EQUAL(static_cast<int16>(10), pickle.ReadInt16());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(10), pickle.ReadUInt16());
        CPPUNIT_ASSERT_EQUAL(static_cast<int32>(0), pickle.ReadInt32());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint32>(0), pickle.ReadUInt32());
        CPPUNIT_ASSERT_EQUAL(static_cast<int32>(100), pickle.ReadInt32());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint32>(100), pickle.ReadUInt32());
        CPPUNIT_ASSERT_EQUAL(static_cast<int64>(0), pickle.ReadInt64());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint64>(0), pickle.ReadUInt64());
        CPPUNIT_ASSERT_EQUAL(static_cast<int64>(1000), pickle.ReadInt64());
        CPPUNIT_ASSERT_EQUAL(static_cast<uint64>(1000), pickle.ReadUInt64());

        CPPUNIT_ASSERT_EQUAL(kMinInt8, pickle.ReadInt8());
        CPPUNIT_ASSERT_EQUAL(kMaxInt8, pickle.ReadInt8());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt8, pickle.ReadUInt8());
        CPPUNIT_ASSERT_EQUAL(kMinInt16, pickle.ReadInt16());
        CPPUNIT_ASSERT_EQUAL(kMaxInt16, pickle.ReadInt16());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt16, pickle.ReadUInt16());
        CPPUNIT_ASSERT_EQUAL(kMinInt32, pickle.ReadInt32());
        CPPUNIT_ASSERT_EQUAL(kMaxInt32, pickle.ReadInt32());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt32, pickle.ReadUInt32());
        CPPUNIT_ASSERT_EQUAL(kMinInt64, pickle.ReadInt64());
        CPPUNIT_ASSERT_EQUAL(kMaxInt64, pickle.ReadInt64());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt64, pickle.ReadUInt64());
    }

    void testFloat() {
        Pickle pickle;

        pickle.WriteFloat(0.0f);
        pickle.WriteFloat(-1.0f);
        pickle.WriteFloat(1.0f);
        pickle.WriteFloat(-100.5f);
        pickle.WriteFloat(100.5f);

        CPPUNIT_ASSERT_EQUAL(static_cast<int>(5 * sizeof(float)),
                             pickle.Size());

        CPPUNIT_ASSERT_EQUAL(0.0f, pickle.ReadFloat());
        CPPUNIT_ASSERT_EQUAL(-1.0f, pickle.ReadFloat());
        CPPUNIT_ASSERT_EQUAL(1.0f, pickle.ReadFloat());
        CPPUNIT_ASSERT_EQUAL(-100.5f, pickle.ReadFloat());
        CPPUNIT_ASSERT_EQUAL(100.5f, pickle.ReadFloat());

        CPPUNIT_ASSERT_EQUAL(static_cast<int>(5 * sizeof(float)),
                             pickle.Size());
    }

    void testDouble() {
        Pickle pickle;

        pickle.WriteDouble(0.0);
        pickle.WriteDouble(-1.0);
        pickle.WriteDouble(1.0);
        pickle.WriteDouble(-100.5);
        pickle.WriteDouble(100.5);

        CPPUNIT_ASSERT_EQUAL(static_cast<int>(5 * sizeof(double)),
                             pickle.Size());

        CPPUNIT_ASSERT_EQUAL(0.0, pickle.ReadDouble());
        CPPUNIT_ASSERT_EQUAL(-1.0, pickle.ReadDouble());
        CPPUNIT_ASSERT_EQUAL(1.0, pickle.ReadDouble());
        CPPUNIT_ASSERT_EQUAL(-100.5, pickle.ReadDouble());
        CPPUNIT_ASSERT_EQUAL(100.5, pickle.ReadDouble());

        CPPUNIT_ASSERT_EQUAL(static_cast<int>(5 * sizeof(double)),
                             pickle.Size());
    }

    void testMixedNumbers() {
        Pickle pickle;

        pickle.WriteBool(true);
        pickle.WriteInt8(kMinInt8);
        pickle.WriteUInt8(kMaxUInt8);
        pickle.WriteInt16(kMinInt16);
        pickle.WriteUInt16(kMaxUInt16);
        pickle.WriteInt32(kMinInt32);
        pickle.WriteUInt32(kMaxUInt32);
        pickle.WriteInt64(kMinInt64);
        pickle.WriteUInt64(kMaxUInt64);
        pickle.WriteFloat(2.5f);
        pickle.WriteDouble(250.25);

        CPPUNIT_ASSERT_EQUAL(true, pickle.ReadBool());
        CPPUNIT_ASSERT_EQUAL(kMinInt8, pickle.ReadInt8());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt8, pickle.ReadUInt8());
        CPPUNIT_ASSERT_EQUAL(kMinInt16, pickle.ReadInt16());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt16, pickle.ReadUInt16());
        CPPUNIT_ASSERT_EQUAL(kMinInt32, pickle.ReadInt32());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt32, pickle.ReadUInt32());
        CPPUNIT_ASSERT_EQUAL(kMinInt64, pickle.ReadInt64());
        CPPUNIT_ASSERT_EQUAL(kMaxUInt64, pickle.ReadUInt64());
        CPPUNIT_ASSERT_EQUAL(2.5f, pickle.ReadFloat());
        CPPUNIT_ASSERT_EQUAL(250.25, pickle.ReadDouble());
    }

    void testBuffer() {
        const int kSize = 1024;
        uint8 write_buffer1[kSize];
        int8 write_buffer2[kSize];
        uint8 read_buffer1[kSize];
        int8 read_buffer2[kSize];

        Pickle pickle;

        for (int i = 0; i < kSize; i++) {
            write_buffer1[i] = i % kMaxUInt8;
        }
        for (int i = 0; i < kSize; i++) {
            write_buffer2[i] = (kMinInt8 + i) % kMaxInt8;
        }

        pickle.WriteFromBuffer(write_buffer1, kSize);
        pickle.WriteFromBuffer(write_buffer2, kSize);

        pickle.ReadIntoBuffer(read_buffer1, kSize);
        pickle.ReadIntoBuffer(read_buffer2, kSize);

        for (int i = 0; i < kSize; i++) {
            CPPUNIT_ASSERT_EQUAL(static_cast<uint8>(i % kMaxUInt8),
                                 read_buffer1[i]);
        }
        for (int i = 0; i < kSize; i++) {
            CPPUNIT_ASSERT_EQUAL(static_cast<int8>((kMinInt8 + i) % kMaxInt8),
                                 read_buffer2[i]);
        }
    }
};

CPPUNIT_TEST_SUITE_REGISTRATION(PickleTest);
